home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Graphics Programming (2nd Edition) / Visual Basic Graphics Programming 2nd Edition.iso / Src / Ch17 / VBRay.bas < prev    next >
BASIC Source File  |  1999-07-09  |  31KB  |  983 lines

  1. Attribute VB_Name = "RayTracing"
  2. Option Explicit
  3.  
  4. Public Running As Boolean
  5.  
  6. ' The collection of objects in the scene.
  7. Public Objects As Collection
  8.  
  9. ' Viewing position.
  10. Public EyeR As Single
  11. Public EyeTheta As Single
  12. Public EyePhi As Single
  13. Public EyeX As Single
  14. Public EyeY As Single
  15. Public EyeZ As Single
  16.  
  17. ' Focus point.
  18. Public FocusX As Single
  19. Public FocusY As Single
  20. Public FocusZ As Single
  21.  
  22. ' Collection of light sources.
  23. Public LightSources As Collection
  24.  
  25. ' Ambient light.
  26. Public AmbientIr As Single
  27. Public AmbientIg As Single
  28. Public AmbientIb As Single
  29.  
  30. ' The background color.
  31. Public BackR As Long
  32. Public BackG As Long
  33. Public BackB As Long
  34. ' Return an interpolated pixel color value.
  35. Public Sub InterpolateColor(ByRef R As Single, ByRef G As Single, ByRef B As Single, pixels() As RGBTriplet, ByVal X As Single, ByVal Y As Single)
  36. Dim ix As Integer
  37. Dim iy As Integer
  38. Dim dx1 As Single
  39. Dim dx2 As Single
  40. Dim dy1 As Single
  41. Dim dy2 As Single
  42. Dim v11 As Integer
  43. Dim v12 As Integer
  44. Dim v21 As Integer
  45. Dim v22 As Integer
  46.  
  47.     ' Find the nearest integral position.
  48.     ix = Int(X)
  49.     iy = Int(Y)
  50.  
  51.     ' See if this is out of bounds.
  52.     If (ix < 0) Or (ix >= UBound(pixels, 1)) Or _
  53.        (iy < 0) Or (iy >= UBound(pixels, 2)) _
  54.     Then
  55.         ' The point is outside the image. Use black.
  56.         R = 0
  57.         G = 0
  58.         B = 0
  59.     Else
  60.         ' The point lies within the image.
  61.         ' Calculate its value.
  62.         dx1 = X - ix
  63.         dy1 = Y - iy
  64.         dx2 = 1# - dx1
  65.         dy2 = 1# - dy1
  66.  
  67.         ' Calculate the red value.
  68.         v11 = pixels(ix, iy).rgbRed
  69.         v12 = pixels(ix, iy + 1).rgbRed
  70.         v21 = pixels(ix + 1, iy).rgbRed
  71.         v22 = pixels(ix + 1, iy + 1).rgbRed
  72.         R = v11 * dx2 * dy2 + v12 * dx2 * dy1 + _
  73.             v21 * dx1 * dy2 + v22 * dx1 * dy1
  74.  
  75.         ' Calculate the green value.
  76.         v11 = pixels(ix, iy).rgbGreen
  77.         v12 = pixels(ix, iy + 1).rgbGreen
  78.         v21 = pixels(ix + 1, iy).rgbGreen
  79.         v22 = pixels(ix + 1, iy + 1).rgbGreen
  80.         G = v11 * dx2 * dy2 + v12 * dx2 * dy1 + _
  81.             v21 * dx1 * dy2 + v22 * dx1 * dy1
  82.  
  83.         ' Calculate the blue value.
  84.         v11 = pixels(ix, iy).rgbBlue
  85.         v12 = pixels(ix, iy + 1).rgbBlue
  86.         v21 = pixels(ix + 1, iy).rgbBlue
  87.         v22 = pixels(ix + 1, iy + 1).rgbBlue
  88.         B = v11 * dx2 * dy2 + v12 * dx2 * dy1 + _
  89.             v21 * dx1 * dy2 + v22 * dx1 * dy1
  90.     End If
  91. End Sub
  92. ' Return the red, green, and blue components of
  93. ' an object at a hit position (px, py, pz) with
  94. ' normal vector (nx, ny, nz).
  95. Public Sub CalculateHitColor(ByVal depth As Integer, _
  96.     Objects As Collection, ByVal target_object As RayTraceable, _
  97.     ByVal eye_x As Single, ByVal eye_y As Single, ByVal eye_z As Single, _
  98.     ByVal px As Single, ByVal py As Single, ByVal pz As Single, _
  99.     ByVal Nx As Single, ByVal Ny As Single, ByVal Nz As Single, _
  100.     ByVal DiffuseKr As Single, ByVal DiffuseKg As Single, ByVal DiffuseKb As Single, _
  101.     ByVal AmbientKr As Single, ByVal AmbientKg As Single, ByVal AmbientKb As Single, _
  102.     ByVal SpecularK As Single, ByVal SpecularN As Single, _
  103.     ByVal Krr As Single, ByVal Krg As Single, ByVal Krb As Single, _
  104.     ByVal is_reflective As Boolean, _
  105.     ByVal Ktr As Single, ByVal Ktg As Single, ByVal Ktb As Single, _
  106.     ByVal TransN As Single, ByVal n1 As Single, ByVal n2 As Single, _
  107.     ByVal is_transparent As Boolean, _
  108.     ByRef R As Integer, ByRef G As Integer, ByRef B As Integer)
  109.  
  110. ' Vectors.
  111. Dim Vx As Single        ' V: P to viewpoint.
  112. Dim Vy As Single
  113. Dim Vz As Single
  114. Dim v_len As Single
  115. Dim lx As Single        ' L: P to light source.
  116. Dim ly As Single
  117. Dim lz As Single
  118. Dim nlx As Single       ' Unit length NL: P to light source.
  119. Dim nly As Single
  120. Dim nlz As Single
  121. Dim l_len As Single
  122. Dim lmx As Single       ' LM: Light source mirror vector.
  123. Dim lmy As Single
  124. Dim lmz As Single
  125. Dim vmx As Single       ' VM: View direction mirror.
  126. Dim vmy As Single
  127. Dim vmz As Single
  128. Dim ltx As Single       ' LT: Light transmission vector.
  129. Dim lty As Single
  130. Dim ltz As Single
  131. Dim vtx As Single       ' VT: Viewing transmission vector.
  132. Dim vty As Single
  133. Dim vtz As Single
  134.  
  135. ' Dot products.
  136. Dim LdotN As Single
  137. Dim VdotN As Single
  138. Dim LMdotV As Single
  139. Dim VdotLT As Single
  140.  
  141. ' Colors
  142. Dim total_r As Single
  143. Dim total_g As Single
  144. Dim total_b As Single
  145. Dim r_refl As Integer
  146. Dim g_refl As Integer
  147. Dim b_refl As Integer
  148. Dim r_tran As Integer
  149. Dim g_tran As Integer
  150. Dim b_tran As Integer
  151.  
  152. Dim light_source As LightSource
  153. Dim shadowed As Boolean
  154. Dim shadow_object As RayTraceable
  155. Dim shadow_t As Single
  156. Dim cos1 As Single
  157. Dim cos2 As Single
  158. Dim cos2_squared As Single
  159. Dim n1_over_n2 As Single
  160. Dim normal_factor As Single
  161. Dim trans_d As Single
  162. Dim distance_factor As Single
  163. Dim diffuse_factor As Single
  164. Dim specular_factor As Single
  165. Dim n_ratio As Single
  166. Dim cos_factor As Single
  167. Dim transmitted_factor As Single
  168.  
  169.     ' Get vector V.
  170.     Vx = eye_x - px
  171.     Vy = eye_y - py
  172.     Vz = eye_z - pz
  173.     v_len = Sqr(Vx * Vx + Vy * Vy + Vz * Vz)
  174.     Vx = Vx / v_len
  175.     Vy = Vy / v_len
  176.     Vz = Vz / v_len
  177.  
  178.     ' Calculate V dot N.
  179.     VdotN = Vx * Nx + Vy * Ny + Vz * Nz
  180.  
  181.     ' ***********
  182.     ' * Ambient *
  183.     ' ***********
  184.     total_r = AmbientIr * AmbientKr
  185.     total_g = AmbientIg * AmbientKg
  186.     total_b = AmbientIb * AmbientKb
  187.  
  188.     ' Consider each light source.
  189.     For Each light_source In LightSources
  190.         ' Find vector L not normalized.
  191.         lx = light_source.TransX - px
  192.         ly = light_source.TransY - py
  193.         lz = light_source.TransZ - pz
  194.  
  195.         ' Get the distance factor for depth queueing.
  196.         l_len = Sqr(lx * lx + ly * ly + lz * lz)
  197.         distance_factor = (light_source.Rmin + light_source.Kdist) / (l_len + light_source.Kdist)
  198.  
  199.         ' Normalize vector L.
  200.         nlx = lx / l_len
  201.         nly = ly / l_len
  202.         nlz = lz / l_len
  203.  
  204.         ' See if the light is on the same side of the
  205.         ' surface as the normal.
  206.         LdotN = nlx * Nx + nly * Ny + nlz * Nz
  207.  
  208.         ' See if the light and viewpoint are on
  209.         ' opposite sides of the surface.
  210.         shadowed = (LdotN * VdotN < 0)
  211.  
  212.         ' See if we are shadowed.
  213.         If Not shadowed Then
  214.             For Each shadow_object In Objects
  215.                 If Not (shadow_object Is target_object) Then
  216.                     ' See where vector L intersects
  217.                     ' the shadow object.
  218.                     shadow_t = shadow_object.FindT( _
  219.                         False, _
  220.                         light_source.TransX, _
  221.                         light_source.TransY, _
  222.                         light_source.TransZ, _
  223.                         -lx, -ly, -lz)
  224.  
  225.                     ' If shadow_t < 1, we're shadowed.
  226.                     If (shadow_t > 0.00001) And (shadow_t < 0.99999) Then
  227.                         shadowed = True
  228.                         Exit For
  229.                     End If
  230.                 End If
  231.             Next shadow_object
  232.         End If
  233.  
  234.         ' We have diffuse and specular components if
  235.         ' the light and viewpoint are on the same
  236.         ' side of the surface normal, and if
  237.         ' we are not shadowed.
  238.         If (LdotN > 0) And (VdotN > 0) And (Not shadowed) Then
  239.             ' The light is shining on the surface.
  240.             ' ***********
  241.             ' * Diffuse *
  242.             ' ***********
  243.             ' There is a diffuse component.
  244.             diffuse_factor = LdotN * distance_factor
  245.             total_r = total_r + light_source.Ir * DiffuseKr * diffuse_factor
  246.             total_g = total_g + light_source.Ig * DiffuseKg * diffuse_factor
  247.             total_b = total_b + light_source.Ib * DiffuseKb * diffuse_factor
  248.  
  249.             ' ************
  250.             ' * Specular *
  251.             ' ************
  252.             ' Find the light mirror vector LM.
  253.             lmx = 2 * Nx * LdotN - nlx
  254.             lmy = 2 * Ny * LdotN - nly
  255.             lmz = 2 * Nz * LdotN - nlz
  256.  
  257.             LMdotV = lmx * Vx + lmy * Vy + lmz * Vz
  258.             If LMdotV > 0 Then
  259.                 specular_factor = SpecularK * (LMdotV ^ SpecularN)
  260.                 total_r = total_r + light_source.Ir * specular_factor
  261.                 total_g = total_g + light_source.Ig * specular_factor
  262.                 total_b = total_b + light_source.Ib * specular_factor
  263.             End If
  264.         End If ' End if the light shines on the surface.
  265.  
  266.         ' **********************
  267.         ' * Direct Transmitted *
  268.         ' **********************
  269.         ' See if the light and viewpoint are on
  270.         ' opposite sides of the surface and if we
  271.         ' are not in shadow.
  272.         If is_transparent Then
  273.             ' Find LT, the light transmission vector.
  274.             If LdotN < 0 Then
  275.                 ' L and N point in opposite directions.
  276.                 ' The ray is leaving the object.
  277.                 n1_over_n2 = n2 / n1
  278.             Else
  279.                 ' L and N point in the same direction.
  280.                 ' The ray is entering the object.
  281.                 n1_over_n2 = n1 / n2
  282.             End If
  283.  
  284.             cos1 = LdotN
  285.             cos2_squared = 1 - (1 - cos1 * cos1) * n1_over_n2 * n1_over_n2
  286.             If cos2_squared > 0 Then
  287.                 cos2 = Sqr(cos2_squared)
  288.                 ' Note that the incident vector I = -L.
  289.                 normal_factor = cos2 - n1_over_n2 * cos1
  290.                 ltx = -n1_over_n2 * nlx - normal_factor * Nx
  291.                 lty = -n1_over_n2 * nly - normal_factor * Ny
  292.                 ltz = -n1_over_n2 * nlz - normal_factor * Nz
  293.  
  294.                 ' Calculate V dot LT.
  295.                 VdotLT = Vx * ltx + Vy * lty + Vz * ltz
  296.  
  297.                 ' See if V and LT point in generally
  298.                 ' the same direction.
  299.                 If VdotLT > 0 Then
  300.                     ' Calculate V dot LT to the TransN.
  301.                     transmitted_factor = VdotLT ^ TransN
  302.  
  303.                     ' Add the direct transmitted component.
  304.                     total_r = total_r + Ktr * light_source.Ir * transmitted_factor
  305.                     total_g = total_g + Ktg * light_source.Ig * transmitted_factor
  306.                     total_b = total_b + Ktb * light_source.Ib * transmitted_factor
  307.                 End If
  308.             End If
  309.         End If
  310.     Next light_source
  311.  
  312.     ' *************
  313.     ' * Reflected *
  314.     ' *************
  315.     If (depth > 1) And is_reflective Then
  316.         ' Find the view mirror vector VM.
  317.         vmx = 2 * Nx * VdotN - Vx
  318.         vmy = 2 * Ny * VdotN - Vy
  319.         vmz = 2 * Nz * VdotN - Vz
  320.  
  321.         ' Trace a ray from (px, py, pz) in the
  322.         ' direction VM.
  323.         TraceRay False, depth - 1, target_object, _
  324.             px, py, pz, vmx, vmy, vmz, _
  325.             r_refl, g_refl, b_refl
  326.  
  327.         ' Multiply by the reflection coefficients.
  328.         total_r = total_r + Krr * r_refl
  329.         total_g = total_g + Krg * g_refl
  330.         total_b = total_b + Krb * b_refl
  331.     End If
  332.  
  333.     ' **************************
  334.     ' * Indirectly Transmitted *
  335.     ' **************************
  336.     ' See if the surface is transparent.
  337.     If (depth > 1) And is_transparent Then
  338.         ' Find VT, the viewing transmission vector.
  339.         cos1 = Abs(VdotN)
  340.         n1_over_n2 = n1 / n2
  341.         cos2_squared = 1 - (1 - cos1 * cos1) * n1_over_n2 * n1_over_n2
  342.         If cos2_squared > 0 Then
  343.             cos2 = Sqr(1 - (1 - cos1 * cos1) * n1_over_n2 * n1_over_n2)
  344.             ' Note that the incident vector I = -V.
  345.             normal_factor = cos2 - n1_over_n2 * cos1
  346.             vtx = -n1_over_n2 * Vx - normal_factor * Nx
  347.             vty = -n1_over_n2 * Vy - normal_factor * Ny
  348.             vtz = -n1_over_n2 * Vz - normal_factor * Nz
  349.  
  350.             TraceRay False, depth - 1, target_object, _
  351.                 px, py, pz, vtx, vty, vtz, _
  352.                 r_tran, g_tran, b_tran
  353.  
  354.             ' Add the indirectly transmitted components.
  355.             total_r = total_r + Ktr * r_tran
  356.             total_g = total_g + Ktg * g_tran
  357.             total_b = total_b + Ktb * b_tran
  358.         End If
  359.     End If
  360.  
  361.     ' For points close to a light source, these
  362.     ' values can be big. Keep them <= 255.
  363.     If total_r > 255 Then total_r = 255
  364.     If total_g > 255 Then total_g = 255
  365.     If total_b > 255 Then total_b = 255
  366.     If total_r < 0 Then total_r = 0
  367.     If total_g < 0 Then total_g = 0
  368.     If total_b < 0 Then total_b = 0
  369.     R = total_r
  370.     G = total_g
  371.     B = total_b
  372. End Sub
  373. ' Return the red, green, and blue components of
  374. ' an object at a hit position (px, py, pz) with
  375. ' normal vector (nx, ny, nz). Consider diffuse,
  376. ' specular, and ambient light components.
  377. Public Sub CalculateHitColorDSA(ByVal depth As Integer, _
  378.     Objects As Collection, ByVal target_object As RayTraceable, _
  379.     ByVal eye_x As Single, ByVal eye_y As Single, ByVal eye_z As Single, _
  380.     ByVal px As Single, ByVal py As Single, ByVal pz As Single, _
  381.     ByVal Nx As Single, ByVal Ny As Single, ByVal Nz As Single, _
  382.     ByVal DiffuseKr As Single, ByVal DiffuseKg As Single, ByVal DiffuseKb As Single, _
  383.     ByVal AmbientKr As Single, ByVal AmbientKg As Single, ByVal AmbientKb As Single, _
  384.     ByVal SpecularK As Single, ByVal SpecularN As Single, _
  385.     ByRef R As Integer, ByRef G As Integer, ByRef B As Integer)
  386.  
  387. ' Vectors.
  388. Dim Vx As Single        ' V: P to viewpoint.
  389. Dim Vy As Single
  390. Dim Vz As Single
  391. Dim v_len As Single
  392. Dim lx As Single        ' L: P to light source.
  393. Dim ly As Single
  394. Dim lz As Single
  395. Dim l_len As Single
  396. Dim lmx As Single       ' LM: Light source mirror vector.
  397. Dim lmy As Single
  398. Dim lmz As Single
  399.  
  400. ' Dot products.
  401. Dim LdotN As Single
  402. Dim VdotN As Single
  403. Dim LMdotV As Single
  404.  
  405. ' Colors
  406. Dim total_r As Single
  407. Dim total_g As Single
  408. Dim total_b As Single
  409.  
  410. Dim light_source As LightSource
  411. Dim shadowed As Boolean
  412. Dim shadow_object As RayTraceable
  413. Dim shadow_t As Single
  414. Dim spec As Single
  415.  
  416.     ' Get vector V.
  417.     Vx = eye_x - px
  418.     Vy = eye_y - py
  419.     Vz = eye_z - pz
  420.     v_len = Sqr(Vx * Vx + Vy * Vy + Vz * Vz)
  421.     Vx = Vx / v_len
  422.     Vy = Vy / v_len
  423.     Vz = Vz / v_len
  424.  
  425.     ' Consider each light source.
  426.     For Each light_source In LightSources
  427.         ' Find vector L not normalized.
  428.         lx = light_source.TransX - px
  429.         ly = light_source.TransY - py
  430.         lz = light_source.TransZ - pz
  431.  
  432.         ' See if we are shadowed.
  433.         shadowed = False
  434.         For Each shadow_object In Objects
  435.             If Not (shadow_object Is target_object) Then
  436.                 ' See where vector L intersects
  437.                 ' ths shadow object.
  438.                 shadow_t = shadow_object.FindT( _
  439.                     False, _
  440.                     light_source.TransX, _
  441.                     light_source.TransY, _
  442.                     light_source.TransZ, _
  443.                     -lx, -ly, -lz)
  444.  
  445.                 ' If shadow_t < 1, we're shadowed.
  446.                 If (shadow_t > 0) And (shadow_t < 1) Then
  447.                     shadowed = True
  448.                     Exit For
  449.                 End If
  450.             End If
  451.         Next shadow_object
  452.  
  453.         ' Normalize vector L.
  454.         l_len = Sqr(lx * lx + ly * ly + lz * lz)
  455.         lx = lx / l_len
  456.         ly = ly / l_len
  457.         lz = lz / l_len
  458.  
  459.         ' See if the viewpoint is on the same
  460.         ' side of the surface as the normal.
  461.         VdotN = Vx * Nx + Vy * Ny + Vz * Nz
  462.  
  463.         ' See if the light is on the same side of the
  464.         ' surface as the normal.
  465.         LdotN = lx * Nx + ly * Ny + lz * Nz
  466.  
  467.         ' We have diffuse and specular components if
  468.         ' the light and viewpoint are on the same
  469.         ' side of the surface as the normal, and if
  470.         ' we are not shadowed.
  471.         If (VdotN >= 0) And (LdotN >= 0) And (Not shadowed) Then
  472.             ' The light is shining on the surface.
  473.             ' ***********
  474.             ' * Diffuse *
  475.             ' ***********
  476.             ' There is a diffuse component.
  477.             total_r = total_r + light_source.Ir * DiffuseKr * LdotN
  478.             total_g = total_g + light_source.Ig * DiffuseKg * LdotN
  479.             total_b = total_b + light_source.Ib * DiffuseKb * LdotN
  480.  
  481.             ' ************
  482.             ' * Specular *
  483.             ' ************
  484.             ' Find the light mirror vector LM.
  485.             lmx = 2 * Nx * LdotN - lx
  486.             lmy = 2 * Ny * LdotN - ly
  487.             lmz = 2 * Nz * LdotN - lz
  488.  
  489.             LMdotV = lmx * Vx + lmy * Vy + lmz * Vz
  490.             If LMdotV > 0 Then
  491.                 spec = SpecularK * (LMdotV ^ SpecularN)
  492.                 total_r = total_r + light_source.Ir * spec
  493.                 total_g = total_g + light_source.Ig * spec
  494.                 total_b = total_b + light_source.Ib * spec
  495.             End If
  496.         End If ' End if the light shines on the surface.
  497.     Next light_source
  498.  
  499.     ' ***********
  500.     ' * Ambient *
  501.     ' ***********
  502.     total_r = total_r + AmbientIr * AmbientKr
  503.     total_g = total_g + AmbientIg * AmbientKg
  504.     total_b = total_b + AmbientIb * AmbientKb
  505.  
  506.     ' For points close to a light source, these
  507.     ' values can be big. Keep them <= 255.
  508.     If total_r > 255 Then total_r = 255
  509.     If total_g > 255 Then total_g = 255
  510.     If total_b > 255 Then total_b = 255
  511.     R = total_r
  512.     G = total_g
  513.     B = total_b
  514. End Sub
  515.  
  516. ' Sort the polygons by Zmin value.
  517. Private Sub QuickSortPolygons(ByVal min As Integer, ByVal max As Integer, polygons() As SimplePolygon)
  518. Dim mid_polygon As SimplePolygon
  519. Dim mid_z As Single
  520. Dim lo As Integer
  521. Dim hi As Integer
  522.  
  523.     ' See if we're done.
  524.     If min >= max Then Exit Sub
  525.  
  526.     Set mid_polygon = polygons(min)
  527.     mid_z = mid_polygon.Zmin
  528.     lo = min
  529.     hi = max
  530.     Do
  531.         ' Look down from hi for a value <= med_z.
  532.         Do While (polygons(hi).Zmin >= mid_z)
  533.             hi = hi - 1
  534.             If hi <= lo Then Exit Do
  535.         Loop
  536.         If hi <= lo Then
  537.             Set polygons(lo) = mid_polygon
  538.             Exit Do
  539.         End If
  540.  
  541.         ' Swap the low and high values.
  542.         Set polygons(lo) = polygons(hi)
  543.  
  544.         ' Look up from lo for a value >= mid_z.
  545.         lo = lo + 1
  546.         Do While (polygons(lo).Zmin < mid_z)
  547.             lo = lo + 1
  548.             If lo >= hi Then Exit Do
  549.         Loop
  550.         If lo >= hi Then
  551.             lo = hi
  552.             Set polygons(hi) = mid_polygon
  553.             Exit Do
  554.         End If
  555.  
  556.         ' Swap the low and high values.
  557.         Set polygons(hi) = polygons(lo)
  558.     Loop
  559.  
  560.     ' Recursively sort the sublists.
  561.     QuickSortPolygons min, lo - 1, polygons
  562.     QuickSortPolygons lo + 1, max, polygons
  563. End Sub
  564. ' Sort the polygons so those with the smallest Z
  565. ' values come first.
  566. Public Sub OrderPolygons(ByVal num_polygons As Integer, polygons() As SimplePolygon)
  567. Dim min_unfixed As Integer
  568. Dim max_z As Single
  569. Dim i As Integer
  570. Dim j As Integer
  571. Dim pgon As SimplePolygon
  572. Dim obscured As Boolean
  573.  
  574.     ' Use QuickSort to sort by minimum Z value.
  575.     ' This gets them mostly in order.
  576.     QuickSortPolygons 1, num_polygons, polygons
  577.  
  578.     ' Fix any small differences.
  579.     min_unfixed = 1
  580.     Do While min_unfixed < num_polygons
  581.         ' Get this polygon's maximum Z value.
  582.         Set pgon = polygons(min_unfixed)
  583.         max_z = pgon.Zmax
  584.  
  585.         ' Examine the following polygons until we come
  586.         ' to one where Zmin >= this polygon's Zmax.
  587.         ' See pgon obscures them.
  588.         obscured = False
  589.         i = min_unfixed + 1
  590.         Do While i <= num_polygons
  591.             ' See if we have checked far enough.
  592.             If (polygons(i).Zmin >= max_z) Then Exit Do
  593.  
  594.             ' See if pgon belongs above polygons(i).
  595.             ' This is true if pgon obscures it.
  596.             If pgon.Obscures(polygons(i)) Then
  597.                 obscured = True
  598.                 Exit Do
  599.             End If
  600.             i = i + 1
  601.         Loop
  602.  
  603.         ' See if we are obscured.
  604.         If obscured Then
  605.             ' We obscure polygons(i).
  606.             ' Move polygons(i) into position
  607.             ' min_unfixed, keeping the other
  608.             ' polygons in order.
  609.             For j = min_unfixed + 1 To i
  610.                 Set polygons(j - 1) = polygons(j)
  611.             Next j
  612.             Set polygons(i) = pgon
  613.         Else
  614.             ' We do not obscure polygons(i).
  615.             ' Pgon is in its correct position.
  616.             min_unfixed = min_unfixed + 1
  617.         End If
  618.     Loop
  619. End Sub
  620. ' Create a projection transformation for a non-ray
  621. ' traced rendering.
  622. Private Sub TransformForNonRayTracing(M() As Single, ByVal pic As PictureBox)
  623. Dim t1(1 To 4, 1 To 4) As Single
  624. Dim t2(1 To 4, 1 To 4) As Single
  625. Dim T3(1 To 4, 1 To 4) As Single
  626. Dim T12(1 To 4, 1 To 4) As Single
  627.  
  628.     ' Rotate the eye onto the Z axis.
  629.     m3PProject t1, project_Parallel, _
  630.         EyeR, EyePhi, EyeTheta, _
  631.         FocusX, FocusY, FocusZ, _
  632.         0, 1, 0
  633.  
  634.     ' Transform the viewing location.
  635.     EyeX = 0
  636.     EyeY = 0
  637.     EyeZ = EyeR
  638.  
  639.     ' Project as if we were ray tracing.
  640.     project_PerspectiveXY t2, EyeR
  641.  
  642.     ' Translate the origin to the center
  643.     ' of the PictureBox.
  644.     m3Translate T3, pic.ScaleWidth / 2, pic.ScaleHeight / 2, 0
  645.  
  646.     ' Combine the transformations.
  647.     m3MatMultiplyFull T12, t1, t2
  648.     m3MatMultiplyFull M, T12, T3
  649. End Sub
  650. ' Return the pixel color given by tracing from
  651. ' point (px, py, pz) in direction <vx, vy, vz>.
  652. Public Sub TraceRay(ByVal direct_calculation As Boolean, ByVal depth As Integer, ByVal skip_object As RayTraceable, ByVal px As Single, ByVal py As Single, ByVal pz As Single, ByVal Vx As Single, ByVal Vy As Single, ByVal Vz As Single, ByRef R As Integer, ByRef G As Integer, ByRef B As Integer)
  653. Dim obj As RayTraceable
  654. Dim best_obj As RayTraceable
  655. Dim best_t As Single
  656. Dim t As Single
  657.  
  658.     ' Find the object that's closest.
  659.     best_t = INFINITY
  660.     For Each obj In Objects
  661.         ' Skip the object skip_object. We use this
  662.         ' to avoid erroneously hitting the object
  663.         ' casting out a ray.
  664.         If Not (obj Is skip_object) Then
  665.             t = obj.FindT(direct_calculation, px, py, pz, Vx, Vy, Vz)
  666.             If (t > 0) And (best_t > t) Then
  667.                 best_t = t
  668.                 Set best_obj = obj
  669.             End If
  670.         End If
  671.     Next obj
  672.  
  673.     ' See if we hit anything.
  674.     If best_obj Is Nothing Then
  675.         ' We hit nothing. Return the background color.
  676.         R = BackR
  677.         G = BackG
  678.         B = BackB
  679.     Else
  680.         ' Compute the color at that point.
  681.         best_obj.FindHitColor depth, Objects, _
  682.             px, py, pz, _
  683.             px + best_t * Vx, _
  684.             py + best_t * Vy, _
  685.             pz + best_t * Vz, _
  686.             R, G, B
  687.  
  688.         ' This is a problem for some values of Kdist.
  689.         If R < 0 Then R = 0
  690.         If G < 0 Then G = 0
  691.         If B < 0 Then B = 0
  692.     End If
  693. End Sub
  694. ' Ray trace on this picture box.
  695. Public Sub TraceAllRays(ByVal pic As PictureBox, ByVal skip As Integer, ByVal depth As Integer)
  696. Dim pixels() As RGBTriplet
  697. Dim bits_per_pixel As Integer
  698. Dim pix_x As Long
  699. Dim pix_y As Long
  700. Dim real_x As Long
  701. Dim real_y As Long
  702. Dim Xmin As Integer
  703. Dim Xmax As Integer
  704. Dim Ymin As Integer
  705. Dim Ymax As Integer
  706. Dim xoff As Integer
  707. Dim yoff As Integer
  708. Dim R As Integer
  709. Dim G As Integer
  710. Dim B As Integer
  711. Dim obj As RayTraceable
  712. Dim Nx As Single
  713. Dim Ny As Single
  714. Dim Nz As Single
  715. Dim dist As Single
  716.  
  717.     If skip < 2 Then
  718.         ' Get the picture box's pixels.
  719.         GetBitmapPixels pic, pixels, bits_per_pixel
  720.     End If
  721.  
  722.     ' Get the transformed coordinates of the eye.
  723.     xoff = pic.ScaleWidth / 2
  724.     yoff = pic.ScaleHeight / 2
  725.     Xmin = pic.ScaleLeft
  726.     Xmax = Xmin + pic.ScaleWidth - 1
  727.     Ymin = pic.ScaleTop
  728.     Ymax = Ymin + pic.ScaleHeight - 1
  729.     For pix_y = Ymin To Ymax Step skip
  730.         real_y = pix_y - yoff
  731.  
  732.         ' The points in this scanline are on the
  733.         ' plane determined by the points:
  734.         '   A: (0, 0, EyeR)
  735.         '   B: (1, Y, 0)
  736.         '   C: (0, Y, 0)
  737.         ' The cross product AB x AC gives a
  738.         ' normal to this plane as:
  739.         '     <1, Y, -EyeR>
  740.         '   x <0, Y, -EyeR>
  741.         '   = <0, EyeR, Y>
  742.         ' Find the unit normal.
  743.         dist = Sqr(EyeR * EyeR + real_y * real_y)
  744.         Nx = 0
  745.         Ny = EyeR / dist
  746.         Nz = real_y / dist
  747.  
  748.         ' Prepare the objects for this scanline.
  749.         For Each obj In Objects
  750.             obj.CullScanline 0, 0, EyeR, Nx, Ny, Nz
  751.         Next obj
  752.  
  753.         For pix_x = Xmin To Xmax Step skip
  754.             real_x = pix_x - xoff
  755.             ' Calculate the value of pixel (x, y).
  756.             ' After transformation the eye is
  757.             ' at (0, 0, EyeR) and the plane of
  758.             ' projection lies in the X-Y plane.
  759.             TraceRay True, depth, Nothing, _
  760.                 0, 0, EyeR, _
  761.                 CSng(real_x), CSng(real_y), -EyeR, _
  762.                 R, G, B
  763.  
  764.             ' Draw the pixel.
  765.             If skip < 2 Then
  766.                 ' Save the pixel value.
  767.                 With pixels(pix_x, pix_y)
  768.                     .rgbRed = R
  769.                     .rgbGreen = G
  770.                     .rgbBlue = B
  771.                 End With
  772.             Else
  773.                 pic.Line (pix_x, pix_y)- _
  774.                     Step(skip - 1, skip - 1), _
  775.                     RGB(R, G, B), BF
  776.             End If
  777.         Next pix_x
  778.  
  779.         ' Let the user see what's going on.
  780.         If skip < 2 Then
  781.             pic.Line (pic.ScaleLeft, pix_y)-(Xmax, pix_y), vbWhite
  782.         Else
  783.             pic.Refresh
  784.         End If
  785.  
  786.         ' If the Stop button was pressed, stop.
  787.         DoEvents
  788.         If Not Running Then Exit For
  789.     Next pix_y
  790.  
  791.     If skip < 2 Then
  792.         SetBitmapPixels pic, bits_per_pixel, pixels
  793.     End If
  794. End Sub
  795. ' Perform a ray tracing.
  796. Public Sub RenderRayTracing(ByVal pic As Object, ByVal skip As Integer, ByVal depth As Integer)
  797. Dim M(1 To 4, 1 To 4) As Single
  798. Dim obj As RayTraceable
  799. Dim light_source As LightSource
  800.  
  801.     ' Rotate the eye onto the Z axis.
  802.     m3PProject M, project_Parallel, _
  803.         EyeR, EyePhi, EyeTheta, _
  804.         FocusX, FocusY, FocusZ, _
  805.         0, 1, 0
  806.  
  807.     ' Transform the viewing location.
  808.     EyeX = 0
  809.     EyeY = 0
  810.     EyeZ = EyeR
  811.  
  812.     ' Transform the objects.
  813.     For Each obj In Objects
  814.         obj.Apply M
  815.     Next obj
  816.  
  817.     ' Transform the light sources.
  818.     For Each light_source In LightSources
  819.         light_source.Apply M
  820.     Next light_source
  821.  
  822.     ' Scale the light intensities for depth queueing.
  823.     ScaleLightSourcesForDepth
  824.  
  825.     ' Trace all the rays.
  826.     TraceAllRays pic, skip, depth
  827. End Sub
  828.  
  829.  
  830. ' Project and draw all the objects.
  831. Public Sub RenderWireFrame(ByVal pic As Object)
  832. Dim M(1 To 4, 1 To 4) As Single
  833. Dim obj As RayTraceable
  834. Dim light_source As LightSource
  835.  
  836.     ' Get the projection transformation.
  837.     TransformForNonRayTracing M, pic
  838.  
  839.     ' Transform the objects.
  840.     For Each obj In Objects
  841.         obj.ApplyFull M
  842.     Next obj
  843.  
  844.     ' Transform the light sources.
  845.     For Each light_source In LightSources
  846.         light_source.ApplyFull M
  847.     Next light_source
  848.  
  849.     ' Draw the wireframes.
  850.     For Each obj In Objects
  851.         obj.DrawWireFrame pic
  852.     Next obj
  853. End Sub
  854. ' Project and draw all the objects with backfaces
  855. ' removed.
  856. Public Sub RenderBackfacesRemoved(ByVal pic As Object)
  857. Dim M(1 To 4, 1 To 4) As Single
  858. Dim obj As RayTraceable
  859.  
  860.     ' Get the projection transformation.
  861.     TransformForNonRayTracing M, pic
  862.  
  863.     ' Transform the objects.
  864.     For Each obj In Objects
  865.         obj.ApplyFull M
  866.     Next obj
  867.  
  868.     ' Draw the wireframes.
  869.     pic.FillStyle = vbFSTransparent
  870.     For Each obj In Objects
  871.         obj.DrawBackfacesRemoved pic
  872.     Next obj
  873. End Sub
  874. ' Project and draw all the objects with hidden
  875. ' surfaces removed.
  876. Public Sub RenderHiddenSurfacesRemoved(ByVal pic As Object, ByVal lblPolygons As Label)
  877. Dim M(1 To 4, 1 To 4) As Single
  878. Dim obj As RayTraceable
  879. Dim num_polygons As Integer
  880. Dim polygons() As SimplePolygon
  881. Dim i As Integer
  882.  
  883.     ' Get the projection transformation.
  884.     TransformForNonRayTracing M, pic
  885.  
  886.     ' Transform the objects.
  887.     For Each obj In Objects
  888.         obj.ApplyFull M
  889.     Next obj
  890.  
  891.     ' Get polygons from the objects.
  892.     For Each obj In Objects
  893.         obj.GetPolygons num_polygons, polygons, False
  894.     Next obj
  895.     lblPolygons.Caption = Format$(num_polygons) & " polygons"
  896.     lblPolygons.Refresh
  897.  
  898.     ' Sort the polygons.
  899.     OrderPolygons num_polygons, polygons
  900.  
  901.     ' Draw the polygons in order.
  902.     pic.FillStyle = vbFSSolid
  903.     For i = 1 To num_polygons
  904.         polygons(i).DrawPolygon pic
  905.     Next i
  906.     pic.Refresh
  907. End Sub
  908. ' Project and draw all the objects with visible
  909. ' shaded surfaces.
  910. Public Sub RenderShaded(ByVal pic As Object, ByVal lblPolygons As Label)
  911. Dim M(1 To 4, 1 To 4) As Single
  912. Dim obj As RayTraceable
  913. Dim light_source As LightSource
  914. Dim num_polygons As Integer
  915. Dim polygons() As SimplePolygon
  916. Dim i As Integer
  917.  
  918.     ' Get the projection transformation.
  919.     TransformForNonRayTracing M, pic
  920.  
  921.     ' Transform the objects.
  922.     For Each obj In Objects
  923.         obj.ApplyFull M
  924.     Next obj
  925.  
  926.     ' Transform the light sources.
  927.     For Each light_source In LightSources
  928.         light_source.ApplyFull M
  929.     Next light_source
  930.  
  931.     ' Scale the light intensities for depth queueing.
  932.     ScaleLightSourcesForDepth
  933.  
  934.     ' Get polygons from the objects.
  935.     For Each obj In Objects
  936.         obj.GetPolygons num_polygons, polygons, True
  937.     Next obj
  938.     lblPolygons.Caption = Format$(num_polygons) & " polygons"
  939.     lblPolygons.Refresh
  940.  
  941.     ' Sort the polygons.
  942.     OrderPolygons num_polygons, polygons
  943.  
  944.     ' Draw the polygons in order.
  945.     pic.FillStyle = vbFSSolid
  946.     For i = 1 To num_polygons
  947.         polygons(i).DrawPolygon pic
  948.     Next i
  949.     pic.Refresh
  950. End Sub
  951.  
  952. ' Set the light sources' Kdist and Rmin values.
  953. Private Sub ScaleLightSourcesForDepth()
  954. Dim light As LightSource
  955.  
  956.     For Each light In LightSources
  957.         ScaleIntensityForDepth light
  958.     Next light
  959. End Sub
  960.  
  961. ' Set this light source's Kdist and Rmin values.
  962. Private Sub ScaleIntensityForDepth(ByVal light As LightSource)
  963. Dim solid As RayTraceable
  964. Dim Rmin As Single
  965. Dim Rmax As Single
  966. Dim new_rmin As Single
  967. Dim new_rmax As Single
  968.  
  969.     Rmin = 1E+30
  970.     Rmax = -1E+30
  971.  
  972.     For Each solid In Objects
  973.         solid.GetRminRmax new_rmin, new_rmax, _
  974.             light.TransX, light.TransY, light.TransZ
  975.         If Rmin > new_rmin Then Rmin = new_rmin
  976.         If Rmax < new_rmax Then Rmax = new_rmax
  977.     Next solid
  978.  
  979.     light.Rmin = Rmin
  980. '    light.Kdist = (Rmax - 5 * Rmin) / 4 ' Fade to 1/5.
  981.     light.Kdist = Rmax - 2 * Rmin ' Fade to 1/2.
  982. End Sub
  983.